﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Globalization;
using BaoJianSoft.Common;
using System.Web.UI.HtmlControls;
using BaoJianSoft.RapidWeb;
using System.IO;

namespace BaoJianSoft.ExtensionModel.Web.Controls
{
	/// <summary>
	/// Class to render a web form for users to input data for specified extension type.
	/// </summary>
	public class ExtensionDataForm : PlaceHolder, INamingContainer
	{
		private static IMetadataApi metadataApi = SpringContext.Current.GetObject<IMetadataApi>();
		private Guid extensionDataTypeId;
		private Dictionary<IFieldMetadata, IExtensionFieldControlBuilder> fieldMetadataControlBuilders = new Dictionary<IFieldMetadata, IExtensionFieldControlBuilder>();

		/// <summary>
		/// Indicates how many columns in a row of rendered table for the form, defaults to TableWith6Columns.
		/// </summary>
		public ExtensionDataFormLayoutTypes LayoutType
		{
			get
			{
				if (this.ViewState["LayoutType"] == null) return ExtensionDataFormLayoutTypes.TableWith6Columns;
				return (ExtensionDataFormLayoutTypes)this.ViewState["LayoutType"];
			}
			set { this.ViewState["LayoutType"] = value; }
		}

		/// <summary>
		/// Append a css class to each table row.
		/// </summary>
		public string TableRowCssClass { get; set; }

		/// <summary>
		/// Gets how many control columns in rendered table.
		/// </summary>
		public int ControlColumnCount
		{
			get
			{
				switch (this.LayoutType)
				{
					case ExtensionDataFormLayoutTypes.TableWith6Columns:
						return 3;

					default:
						throw new NotImplementedException();
				}
			}
		}

		/// <summary>
		/// Initialize field controls by metadata type id.
		/// That means the property "ExtensionDataTypeId" has to be set before the control loaded.
		/// If the property "ExtensionDataTypeId" equals to Guid.Empty, the <see cref="InvalidProgramException"/> will be thrown.
		/// </summary>
		/// <param name="e"></param>
		public void CreateDataInputForm(Guid extensionDataTypeId)
		{
			this.extensionDataTypeId = extensionDataTypeId;

			if (extensionDataTypeId == Guid.Empty)
				throw new InvalidProgramException(@"The argument ""extensionDataTypeId"" should be set before the control loaded.");

			IObjectMetadata objectMetadata = metadataApi.GetType(extensionDataTypeId);
			if (objectMetadata == null)
				throw new InvalidProgramException(@"The argument ""extensionDataTypeId"" is invalid.");

			IEnumerable<IFieldMetadata> fieldMetadataEnumerable = metadataApi.GetFields(extensionDataTypeId);
			if (fieldMetadataEnumerable.Count() == 0) return;

			HtmlTableRow tableRow = new HtmlTableRow();
			tableRow.Attributes["class"] = this.TableRowCssClass;
			this.Controls.Add(tableRow);

			HtmlTableCell lastControlCell = null;
			int occupiedCellsOfThisLine = 0;

			foreach (IFieldMetadata fieldMetadata in fieldMetadataEnumerable)
			{
				string controlBuilderTypeName = string.Format(CultureInfo.InvariantCulture, "BaoJianSoft.ExtensionModel.Web.{0}ExtensionFieldControlBuilder, BaoJianSoft.ExtensionModel.Web", fieldMetadata.Type);
				Type type = Kit.GetType(controlBuilderTypeName);
				IExtensionFieldControlBuilder controlBuilder = Activator.CreateInstance(type) as IExtensionFieldControlBuilder;
				this.fieldMetadataControlBuilders.Add(fieldMetadata, controlBuilder);

				ExtensionDataInputControl dataInputControl = controlBuilder.BuildDataInputControl(fieldMetadata);

				int occupiedControls = dataInputControl.OccupiedControlCells;
				if (occupiedControls > this.ControlColumnCount)
				{
					if (occupiedCellsOfThisLine > 0)
					{
						if (lastControlCell != null)
							lastControlCell.ColSpan += (this.ControlColumnCount - occupiedCellsOfThisLine) * 2;

						tableRow = new HtmlTableRow();
						tableRow.Attributes["class"] = this.TableRowCssClass;
						this.Controls.Add(tableRow);
					}

					occupiedCellsOfThisLine = this.ControlColumnCount;
				}
				else if ((occupiedControls + occupiedCellsOfThisLine) > this.ControlColumnCount)
				{
					if (lastControlCell != null)
						lastControlCell.ColSpan += (this.ControlColumnCount - occupiedCellsOfThisLine) * 2;

					occupiedCellsOfThisLine = occupiedControls;
					tableRow = new HtmlTableRow();
					tableRow.Attributes["class"] = this.TableRowCssClass;
					this.Controls.Add(tableRow);
				}
				else
					occupiedCellsOfThisLine += occupiedControls;

				lastControlCell = this.CreateControlCell(tableRow, fieldMetadata, dataInputControl);
			}

			this.AddBlankTableCells(tableRow, occupiedCellsOfThisLine, this.ControlColumnCount);
		}

		/// <summary>
		/// Reset control values to default by field metadata.
		/// </summary>
		public void ResetControlValuesToDefault()
		{
			foreach (IFieldMetadata fieldMetadata in this.fieldMetadataControlBuilders.Keys)
				this.fieldMetadataControlBuilders[fieldMetadata].Value = fieldMetadata.GetDefaultValue();
		}

		/// <summary>
		/// Set control value from extension business object.
		/// </summary>
		/// <param name="extensionBizObject"></param>
		public void SetControlValuesFromObjectProperties(IExtensionBizObject extensionBizObject)
		{
			Dictionary<string, IFieldMetadata> fieldMetadataDictionary = this.fieldMetadataControlBuilders.Keys.ToDictionary(kvp => kvp.Name, kvp => kvp);
			IEnumerator<KeyValuePair<string, object>> iterator = extensionBizObject.GetFieldEnumerator();
			while (iterator.MoveNext())
			{
				KeyValuePair<string, object> property = iterator.Current;
				if (!fieldMetadataDictionary.ContainsKey(property.Key)) continue;
				IFieldMetadata fieldMetadata = fieldMetadataDictionary[property.Key];
				IExtensionFieldControlBuilder fieldControlBuilder = this.fieldMetadataControlBuilders[fieldMetadata];
				fieldControlBuilder.Value = property.Value;
			}
		}

		/// <summary>
		/// Set control value from extension object.
		/// </summary>
		/// <param name="extensionObject"></param>
		public void SetControlValuesFromObjectProperties(IExtensionObject extensionObject)
		{
			Dictionary<string, IFieldMetadata> fieldMetadataDictionary = this.fieldMetadataControlBuilders.Keys.ToDictionary(kvp => kvp.Name, kvp => kvp);
			IEnumerator<KeyValuePair<string, object>> iterator = extensionObject.GetFieldEnumerator();
			while (iterator.MoveNext())
			{
				KeyValuePair<string, object> property = iterator.Current;
				if (!fieldMetadataDictionary.ContainsKey(property.Key)) continue;
				IFieldMetadata fieldMetadata = fieldMetadataDictionary[property.Key];
				IExtensionFieldControlBuilder fieldControlBuilder = this.fieldMetadataControlBuilders[fieldMetadata];
				fieldControlBuilder.Value = property.Value;
			}
		}

		/// <summary>
		/// Set properties of the extension business object from controls.
		/// </summary>
		/// <param name="extensionBizObject"></param>
		public void SetObjectPropertiesFromControlValues(IExtensionBizObject extensionBizObject)
		{
			Dictionary<string, IFieldMetadata> fieldMetadataDictionary = fieldMetadataControlBuilders.Keys.ToDictionary(kvp => kvp.Name, kvp => kvp);
			foreach (string fieldName in fieldMetadataDictionary.Keys)
			{
				IFieldMetadata fieldMetadata = fieldMetadataDictionary[fieldName];
				IExtensionFieldControlBuilder fieldControlBuilder = this.fieldMetadataControlBuilders[fieldMetadata];
				extensionBizObject[fieldName] = fieldControlBuilder.Value;
			}
		}

		/// <summary>
		/// Set properties of the extension object from controls.
		/// </summary>
		/// <param name="extensionObject"></param>
		public void SetObjectPropertiesFromControlValues(IExtensionObject extensionObject)
		{
			Dictionary<string, IFieldMetadata> fieldMetadataDictionary = fieldMetadataControlBuilders.Keys.ToDictionary(kvp => kvp.Name, kvp => kvp);
			foreach (string fieldName in fieldMetadataDictionary.Keys)
			{
				IFieldMetadata fieldMetadata = fieldMetadataDictionary[fieldName];
				IExtensionFieldControlBuilder fieldControlBuilder = this.fieldMetadataControlBuilders[fieldMetadata];
				extensionObject[fieldName] = fieldControlBuilder.Value;
			}
		}

		private HtmlTableCell CreateControlCell(HtmlTableRow row, IFieldMetadata fieldMetadata, ExtensionDataInputControl dataInputControl)
		{
			HtmlTableCell labelCell = new HtmlTableCell("td");
			labelCell.Attributes["class"] = "c1";
			labelCell.NoWrap = true;
			labelCell.InnerText = fieldMetadata.Name + ": ";
			row.Cells.Add(labelCell);

			HtmlTableCell controlCell = new HtmlTableCell("td");
			HtmlTableCell lastControlCell = controlCell;
			controlCell.Attributes["class"] = "c2";
			controlCell.NoWrap = true;
			controlCell.ColSpan = dataInputControl.OccupiedControlCells != int.MaxValue ? dataInputControl.OccupiedControlCells * 2 - 1 : 5;
			row.Cells.Add(controlCell);

			controlCell.Controls.Add(dataInputControl.Control);
			return lastControlCell;
		}

		private void AddBlankTableCells(HtmlTableRow tableRow, int occupiedCellsOfThisLine, int maxControlsOfTheLine)
		{
			for (int i = 0; i < maxControlsOfTheLine - occupiedCellsOfThisLine; i++)
			{
				string spacerImageUrl = Path.Combine(WebUtility.WebSiteBaseUrl, "resources/images/spacer.gif");

				HtmlTableCell labelCell = new HtmlTableCell("td");
				labelCell.Attributes["class"] = "c1";
				labelCell.NoWrap = true;
				labelCell.Controls.Add(new LiteralControl("<img src='" + spacerImageUrl + "' alt='' />"));
				tableRow.Cells.Add(labelCell);

				HtmlTableCell controlCell = new HtmlTableCell("td");
				controlCell.Attributes["class"] = "c2";
				controlCell.Controls.Add(new LiteralControl("<img src='" + spacerImageUrl + "' alt='' />"));
				tableRow.Cells.Add(controlCell);
			}
		}
	}

	/// <summary>
	/// Layout types of extension data form.
	/// </summary>
	public enum ExtensionDataFormLayoutTypes
	{
		/// <summary>
		/// Table with 6 columns.
		/// </summary>
		TableWith6Columns = 0
	}
}
